﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CloudAppMonitor.Properties;
using System.Threading;
using Microsoft.Samples.ServiceHosting.StorageClient;
using CloudAppMonitor.Library;
using System.Data.Services.Client;
using CloudAppMonitor.Data.EntityData;

namespace CloudAppMonitor.Data
{
    internal class AzureDataDownload
    {
        ///Exposes the current download status to the UI
        public string DownloadStatus { get; protected set; }
        ///True if there are updates taking place
        public bool UpdatingPerformanceCounters { get; protected set; }
        ///The token changes when the updates are done as a signal to refresh the UI
        public Guid UpdateToken { get; protected set; }

        Settings _settings;
        SqlStorage _storage;
        Thread _downloadJob;

        /// <summary>
        /// Starts the background job
        /// </summary>
        /// <param name="settings"></param>
        public AzureDataDownload(Settings settings)
        {
            _settings = settings;

            if (!string.IsNullOrEmpty(_settings.DatabaseName) && !string.IsNullOrEmpty(_settings.DatabaseServer))
            {
                _storage = new SqlStorage(settings);
            }

            _downloadJob = new Thread(new ParameterizedThreadStart(DownloadJob));
            _downloadJob.Name = "AzureDownloadJob";
            _downloadJob.IsBackground = true;
            _downloadJob.Priority = ThreadPriority.BelowNormal;
            _downloadJob.Start();
        }

        /// <summary>
        /// Returns a table context for accessing the cloud
        /// </summary>
        /// <returns></returns>
        private CloudDataServiceContext GetTableDataContext()
        {
            StorageAccountInfo storageAccount = new StorageAccountInfo(new Uri(_settings.AzureTableUrl),
                null,
                _settings.AzureAccountName,
                _settings.AzureSharedApiKey);

            CloudDataServiceContext ctx = new CloudDataServiceContext(storageAccount);
            ctx.RetryPolicy = RetryPolicies.RetryN(3, TimeSpan.FromSeconds(1));
            ctx.Timeout = 600;

            return ctx;
        }

        public void UpdateStorage()
        {
            _storage = new SqlStorage(_settings);
        }

        public void DownloadJob(object context)
        {
            DownloadStatus = "Starting";

            while (true)
            {
                try
                {
                    if (!string.IsNullOrEmpty(_settings.AzureSharedApiKey))
                    {
                        if (!string.IsNullOrEmpty(_settings.AzureTableUrl))
                        {
                            //Get the current counters
                            CloudDataServiceContext ctx = GetTableDataContext();

                            DownloadCloudDiagnosticData<WindowsEventLogsTableEntity>(ctx, ctx.WindowsEventLogs);
                            DownloadCloudDiagnosticData<WindowsAzureDiagnosticLogs>(ctx, ctx.WindowsAzureDiagnosticLogs);

                            UpdatingPerformanceCounters = true;
                            DownloadCloudDiagnosticData<PerformanceCountersTableEntity>(ctx, ctx.PerformanceCountersTable);
                            UpdatingPerformanceCounters = false;
                        }
                    }

                    DownloadStatus = "Waiting...";
                }
                catch (Exception ex)
                {
                    Logging.LogException("DownloadPerformanceCounters", ex);

                    DownloadStatus = string.Format("Failed: {0}", ex.Message);
                }

                UpdatingPerformanceCounters = false;
                UpdateToken = Guid.NewGuid();

                Thread.Sleep(TimeSpan.FromMinutes(_settings.AzureDownloadInterval));
            }
        }

        /// <summary>
        /// Downloads data from the cloud and stores it in the local SQL database
        /// </summary>
        private void DownloadCloudDiagnosticData<T>(CloudDataServiceContext ctx, IQueryable<T> tableQuery) where T : TableStorageEntity
        {
            int totalRecords = 0;

            var data = (from d in tableQuery
                        select d).Take(200);

            TableStorageDataServiceQuery<T> q =
                new TableStorageDataServiceQuery<T>(data as DataServiceQuery<T>,
                    ctx.RetryPolicy);

            //Keep track of the items so they can be deleted later
            List<T> entitiesToDelete = new List<T>();

            try
            {
                //Loop through all the data we found
                foreach (T entity in q.ExecuteAllWithRetries())
                {
                    //Store the counter
                    _storage.StoreEntity(entity);

                    entitiesToDelete.Add(entity);

                    totalRecords++;

                    if (totalRecords % 20 == 0)
                    {
                        DownloadStatus = string.Format("Downloaded {0} {1}", totalRecords, entity.GetType().Name);
                    }
                }
            }
            finally
            {
                //Delete all the data we downloaded and stored in the database
                int totalDeletes = 0;

                foreach (T entity in entitiesToDelete)
                {
                    ctx.DeleteObject(entity);

                    if (totalDeletes % 20 == 0)
                    {
                        DownloadStatus = string.Format("Deleted {0} of {1} {2}", totalDeletes, totalRecords, entity.GetType().Name);

                        ctx.SaveChanges();
                    }

                    totalDeletes++;
                }

                ctx.SaveChanges();
            }

            UpdateToken = Guid.NewGuid();
        }
    }
}
